/*
 * data_table.h
 *
 *  Created on: 2008-8-20
 *      Author: fanliangliang
 */

#ifndef DATA_TABLE_H_
#define DATA_TABLE_H_

#include "config.h"
#include "any.h"
#include <string>
#include <vector>
#include <ostream>
#include <limits>

BEGIN_NAMESPACE_TOXIC

using std::string;
using std::vector;
using std::ostream;

class DataRow {
public:
	DataRow() {
	}

	~DataRow() {
		size_t size = _data.size();
		for (size_t i = 0; i < size; ++i) {
			delete _data[i];
		}
	}

	template <typename T>
	void push_back(const T& value) {
		_data.push_back(new any(value));
	}

	any& at(size_t index) {
		return *_data.at(index);
	}

	const any& at(size_t index) const {
		return *_data.at(index);
	}

	any& operator[] (size_t index) {
		return *_data[index];
	}

	const any& operator[] (size_t index) const {
		return *_data[index];
	}

	size_t size() const {
		return _data.size();
	}

	bool empty() const {
		return _data.empty();
	}

private:
	vector<any*> _data;
};


enum AlignType {
	ALIGN_LEFT,
	ALIGN_RIGHT,
	ALIGN_CENTER
};


class ColumnStyle {
public:
	ColumnStyle(const string& header = "",
			AlignType contentAlign = ALIGN_LEFT,
			AlignType headerAlign = ALIGN_LEFT,
			int maxWidth = -1)
	:_header(header)
	,_contentAlign(contentAlign)
	,_headerAlign(headerAlign)
	,_maxWidth(maxWidth) {
	}

	~ColumnStyle() {
	}

	void setHeader(const string& header) {
		_header = header;
	}

	void setHeaderAlign(AlignType headerAlign) {
		_headerAlign = headerAlign;
	}

	void setContentAlign(AlignType contentAlign) {
		_contentAlign = contentAlign;
	}

	void setMaxWidth(size_t width) {
		_maxWidth = width;
	}

	const string& header() const {
		return _header;
	}

	AlignType headerAlign() const {
		return _headerAlign;
	}

	AlignType contentAlign() const {
		return _contentAlign;
	}

	size_t maxWidth() const {
		return _maxWidth > 0 ? _maxWidth : std::numeric_limits<size_t>::max();
	}

	size_t headerWidth() const {
		return _header.size();
	}

private:
	string _header;
	AlignType _contentAlign;
	AlignType _headerAlign;
	size_t _maxWidth;
};


class DataTable {
public:
	class DataColumn {
	private:
		DataColumn(DataTable* table, size_t index)
		:_table(table)
		,_index(index) {
		}

	public:
		DataColumn(const DataColumn& other)
		:_table(other._table)
		,_index(other._index) {
		}

		~DataColumn() {
		}

		any& at(size_t index) {
			return _table->at(index, _index);
		}

		const any& at(size_t index) const {
			return _table->at(index, _index);
		}

		any& operator[] (size_t index) {
			return (*_table)[index][_index];
		}

		const any& operator[] (size_t index) const {
			return (*_table)[index][_index];
		}

		size_t size() const {
			return _table->rows();
		}

		bool empty() const {
			return size() == 0;
		}

		size_t maxWidth() const {
			size_t max = 0;
			size_t numRow = size();
			for (size_t row = 0; row < numRow; ++row) {
				size_t width = at(row).width();
				if (width > max) {
					max = width;
				}
			}
			return max;
		}

	private:
		DataTable* _table;
		size_t _index;

	private:
		friend class DataTable;
	};

public:
	DataTable();
	~DataTable();

	void push_back(DataRow* row) {
		_data.push_back(row);
	}

	void addColumn(const string& header = "",
			AlignType contentAlign = ALIGN_LEFT,
			AlignType headerAlign = ALIGN_LEFT,
			int maxWidth = -1) {
		_styles.push_back(new ColumnStyle(
				header, contentAlign,
				headerAlign, maxWidth));
	}

	any& at(size_t row, size_t col) {
		return _data.at(row)->at(col);
	}

	any& at(size_t row, size_t col) const {
		return _data.at(row)->at(col);
	}

	DataRow& rowAt(size_t index) {
		return *_data.at(index);
	}

	const DataRow& rowAt(size_t index) const {
		return *_data.at(index);
	}

	DataRow& operator[](size_t index) {
		return *_data[index];
	}

	const DataRow& operator[](size_t index) const {
		return *_data[index];
	}

	DataColumn columnAt(size_t index) const {
		return DataColumn(const_cast<DataTable*>(this), index);
	}

	size_t rows() const {
		return _data.size();
	}

	size_t columns() const {
		return _styles.size();
	}

	bool empty() const {
		return _data.empty();
	}

	bool rowSeparatorEnabled() const;
	void setRowSeparatorEnabled(bool enable);

private:
	size_t maxColumnWidth(size_t col) const;
	size_t idealColumnWidth(size_t col) const;

	void printHorizontalLine(ostream& ostrm, bool endline = true) const;
	void printRow(size_t row, ostream& ostrm) const;
	void printHeader(ostream& ostrm) const;

private:
	vector<DataRow*> _data;
	vector<ColumnStyle*> _styles;
	bool _rowSeparatorEnabled;

private:
	friend ostream& operator<<(ostream& ostrm, const DataTable& table);
};

typedef DataTable::DataColumn DataColumn;

ostream& operator<<(ostream& ostrm, const DataTable& table);

END_NAMESPACE_TOXIC

#endif /* DATA_TABLE_H_ */
